// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using svs.Mobile.BeyondMedia.Controls.Players;
using svs.Mobile.BeyondMedia.Controls.Players.WindowsMediaPlayer;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.Helpers;
using svs.Mobile.BeyondMedia.FeedCore.RSS;
using svs.Mobile.BeyondMedia.Objects;
using svs.Mobile.BeyondMedia.Properties;
using svs.Mobile.BeyondMedia.UiCore;
using svs.Mobile.BeyondMedia.UiCore.GDI;

namespace svs.Mobile.BeyondMedia.Controls
{
	public partial class MediaPlayerView : UserControl, IView
	{
		#region Data Members

		public delegate void MediaPlayerPlayStateChangedEvent(Track track);

		public event Helpers.NavigationEvent Navigation;
		public event EventHandler BackClick;
		public event MediaPlayerPlayStateChangedEvent MediaPlayerPlayStateChanged;
		

		private TrackList m_PlayList = new TrackList();
		private CoreHelper.PlayerView m_CurrentView = Configuration.PlayerView;
		private IPlayerControl MediaPlayer = new NullPlayer();
		private ISkinManager m_MediaPlayerSkinManager = new WMPSkinManager();

		private Track _trackToPlayOnActivate;

		#endregion

		#region Constructors

		public MediaPlayerView()
		{
			InitializeComponent();
			PlayList.KeyDown += OnKeyDown;
			PlayList.TrackSelected += OnPlayListTrackSelected;

			FeedRepository.BeforeTrackDelete += OnFeedRepositoryBeforeTrackDelete;
			FeedRepository.TrackDeleted += OnFeedRepositoryTrackDeleted;

			PlayList.BackClick += OnPlayListBackButtonClick;
		}

		#endregion

		#region Private Methods

		#region Playr Event Handler

		/// <summary>
		/// Invoked when the current media stream ends
		/// </summary>
		/// 
		private void OnPlayerMediaEnded(object sender, EventArgs e)
		{
			//Make sure that we mark the track as played!
			if (CurrentTrack.TotalTime > 0)
				CurrentTrack.MarkPlayed();

			//Mark the RssItem for this track as "read"
			RssFeedCache.SyncFeedReadStatusForTrack(CurrentTrack);

			switch (Configuration.MediaEndAction)
			{
				case CoreHelper.MediaEndAction.DoNothing:
					MediaPlayer.CloseStream();
					break;

				case CoreHelper.MediaEndAction.PlayNext:
					CommandManager.CmdPlayNextTrack();
					break;

				case CoreHelper.MediaEndAction.DeleteAndPlayNext:

					if (CurrentTrack.TrackType == Track.StreamType.LocalFile)
						CommandManager.CmdDeleteCurrentAndPlayNext();
					else
						CommandManager.CmdPlayNextTrack();
					break;
			}
		}

		private void OnPlayerStreamPositionChanged(object sender, EventArgs e)
		{
			if (IsPlaying)
				SaveCurrentPosition(Track.PlayMode.Play);
		}

		private void OnPlayerPlayError(object sender, EventArgs e)
		{
			OnTrackPlayStateChanged(Track.PlayMode.Stop);

			if (MediaPlayerPlayStateChanged != null)
				MediaPlayerPlayStateChanged(CurrentTrack);
		}

		/// <summary>
		/// Invoked when the current media stream starts playing
		/// </summary>
		private void OnPlayerStartPlaying(object sender, EventArgs e)
		{
			SaveCurrentPosition(Track.PlayMode.Play);

			if (MediaPlayerPlayStateChanged != null)
				MediaPlayerPlayStateChanged(CurrentTrack);
		}


		/// <summary>
		/// Invoked when the current media stream is paused
		/// </summary>
		private void OnPlayerPaused(object sender, EventArgs e)
		{
			SaveCurrentPosition(Track.PlayMode.Pause);

			if (MediaPlayerPlayStateChanged != null)
				MediaPlayerPlayStateChanged(CurrentTrack);
		}

		/// <summary>
		/// Invoked when the current media stream is stopped
		/// </summary>
		private void OnPlayerStopped(object sender, EventArgs e)
		{
			OnTrackPlayStateChanged(Track.PlayMode.Stop);

			if (MediaPlayerPlayStateChanged != null)
				MediaPlayerPlayStateChanged(CurrentTrack);
		}

		#endregion

		#region Internal Event Handlers

		private void OnPlayListBackButtonClick(object sender, EventArgs e)
		{
			if (BackClick != null)
				BackClick(this, null);
		}

		private void OnNavigation(Helpers.NavigationDirection i_direction)
		{
			if (Navigation != null)
				Navigation(this, i_direction);
		}

		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			if (e.KeyChar == (Char) Keys.Escape)
			{
				e.Handled = true;
				if (BackClick != null)
					BackClick(this, null);
			}
		}

		/// <summary>
		/// Called when [a key is pressed].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="T:System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
		private void OnKeyDown(object sender, KeyEventArgs e)
		{
			//Left 
			if ((e.KeyCode == Keys.Left))
			{
				if (!IsPlaying)
				{
					OnNavigation(Helpers.NavigationDirection.Back);
					e.Handled = true;
				}
			}

			//Right
			if ((e.KeyCode == Keys.Right))
			{
				if (!IsPlaying)
				{
					OnNavigation(Helpers.NavigationDirection.Forward);
					e.Handled = true;
				}
			}

			if (!e.Handled)
				ShortcutKeyManager.HandleKey(e.KeyCode);
		}

		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);
			SetView();
		}

		private delegate void TrackUpdatedDelegate(Track i_track);
		private void OnFeedRepositoryTrackDeleted(Track i_track)
		{
			if (InvokeRequired)
			{
				TrackUpdatedDelegate cbDel = OnFeedRepositoryTrackDeleted;
				Invoke(cbDel, i_track);
				return;
			}

			if (CurrentTrack == i_track)
				MediaPlayer.CurrentTrack = null;

			if(m_PlayList.Contains(i_track))
			{
				m_PlayList.Remove(i_track);
			}
		}

		private void OnFeedRepositoryBeforeTrackDelete(Track i_track)
		{
			if (InvokeRequired)
			{
				TrackUpdatedDelegate cbDel = OnFeedRepositoryBeforeTrackDelete;
				Invoke(cbDel, i_track);
				return;
			}

			//Make sure we release the track for deletion
			if (CurrentTrack == i_track)
			{
				Close();
			}
		}

		private void OnPlayListTrackSelected(Track aTrack)
		{
			PlayTrack(aTrack);
		}

		#endregion

		private IPlayerControl BuildPlayerControl()
		{
			WindowsMediaPlayerControl Player = new WindowsMediaPlayerControl();
			Player.Anchor = AnchorStyles.None;
			Player.Location = new Point(15, 14);
			Player.Name = "Player";
			Player.Size = new Size(128, 85);
			Player.TabIndex = 10;
			Player.TabStop = false;
			Player.Text = "BeyondPod Player";
			Controls.Add(Player);

			return Player;
		}

		/// <summary>
		/// Skips in the specified i_direction.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		/// <param name="i_duration">The i_duration.</param>
		private void Skip(Helpers.MoveDirection i_direction, int i_duration)
		{
			MediaPlayer.Skip(i_direction, i_duration);

			if (!IsPlaying)
			{
				SaveCurrentPosition(Track.PlayMode.Pause);
			}
		}

		/// <summary>
		/// Saves the current position in the current file.
		/// </summary>
		private void SaveCurrentPosition(Track.PlayMode i_mode)
		{
			CoreHelper.WriteTraceEntry("Saving Position");

			MediaPlayer.SaveCurrentPosition(i_mode);
			OnTrackPlayStateChanged(i_mode);

		}

		private void OnTrackPlayStateChanged(Track.PlayMode i_mode)
		{
			if (CurrentTrack != null)
			{
				CurrentTrack.CurrentPlayState = i_mode;
			}
		}

		private void SetView()
		{
			MediaPlayer.Visible = false;

			switch (m_CurrentView)
			{
				case CoreHelper.PlayerView.FullScreen:
					MediaPlayer.Height = Height;
					break;

				case CoreHelper.PlayerView.Normal:
					MediaPlayer.Height = Height;
					break;

				case CoreHelper.PlayerView.Small:
					MediaPlayer.Height = PlayerSkinManager.CurrentPlayerSkin.SkinSize.Height;
					break;
			}

			//Playlist on top
			PlayList.Top = PlayList.Left = 0;
			PlayList.Width = Width;
			MediaPlayer.Width = PlayList.Width;

			MediaPlayer.Left = 0;

			int plHeight = Height - MediaPlayer.Height;

			MediaPlayer.Top = plHeight + 1;

			if (plHeight < 20)
				PlayList.Visible = false;
			else
			{
				PlayList.Visible = true;
				PlayList.Height = plHeight;
			}

			//Player on top
			/*MediaPlayer.Top = MediaPlayer.Left = 0;
			MediaPlayer.Width = Width;

			PlayList.Left = 0;
			PlayList.Width = MediaPlayer.Width;
			PlayList.Top = MediaPlayer.Bottom + 1;

			int plHeight = Height - MediaPlayer.Height;

			if (plHeight < 20)
				PlayList.Visible = false;
			else
			{
				PlayList.Visible = true;
				PlayList.Height = plHeight;
			}*/

			MediaPlayer.Visible = true;
		}

		private bool IsNewPlaylist(TrackList list)
		{
			if(m_PlayList.Count != list.Count)
				return true;

			for(int i = 0; i != m_PlayList.Count; i++)
			{
				if(m_PlayList[i] != list[i])
					return true;
			}

			return false;
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Gets a screenshot of the view for transitions
		/// </summary>
		/// <returns></returns>
		public Bitmap CreateScreenShot()
		{
			return PlayList.CreateScreenShot(ClientSize);
		}

		public void OnAfterDeactivate(){}

		public void OnAfterActivate()
		{
			if (_trackToPlayOnActivate != null)
			{
				PlayTrack(_trackToPlayOnActivate);
				_trackToPlayOnActivate = null;
			}
		}

		public void OnBeforeActivate()
		{
			if (!IsPlayerLoaded)
			{
				try
				{
					Cursor.Current = Cursors.WaitCursor;
					MediaPlayer = BuildPlayerControl();
					VolumeLevel = Configuration.VolumeLevel;
					SetView();

					PlayerSkinManager.SkinChanged += MediaPlayer.OnSkinChanged;
					MediaPlayer.BackClick += OnPlayListBackButtonClick;
					MediaPlayer.StreamPositionChange += OnPlayerStreamPositionChanged;
					MediaPlayer.PlayError += OnPlayerPlayError;
					MediaPlayer.MediaEnd += OnPlayerMediaEnded;
					MediaPlayer.StartPlaying += OnPlayerStartPlaying;
					MediaPlayer.Paused += OnPlayerPaused;
					MediaPlayer.Stopped += OnPlayerStopped;
				}
				catch
				{
					MessageBoxEx.Show(Resources.WrongMediaPlayerVersion);
				}
				finally
				{
					Cursor.Current = Cursors.Default;
				}
			}
		}

		/// <summary>
		/// Skips a short interval forward or backward.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		public void SkipShort(Helpers.MoveDirection i_direction)
		{
			Skip(i_direction, Configuration.ShortSkipInterval);
		}

		/// <summary>
		///Skips a long interval forward or backward.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		public void SkipLong(Helpers.MoveDirection i_direction)
		{
			Skip(i_direction, Configuration.LongSkipInterval);
		}

		/// <summary>
		/// Gets the next or previous track.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		/// <returns></returns>
		public Track  GetNextPrevTrack(Helpers.MoveDirection i_direction)
		{
			int c;

			if (CurrentTrack != null)
			{
				c = m_PlayList.IndexOf(CurrentTrack);
			}
			else if (m_PlayList.Count > 0)
				c = m_PlayList.Count;
			else
				return null;

			if (i_direction == Helpers.MoveDirection.Forward)
			{
				c++;
				if (c <= m_PlayList.Count - 1)
				{
					return m_PlayList[c];
				}
			}
			if (i_direction == Helpers.MoveDirection.Back)
			{
				c--;
				if (c >= 0)
				{
					return m_PlayList[c];
				}
			}

			return null;
		}

		/// <summary>
		/// Skips a track forward or backward.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		public void SkipTrack(Helpers.MoveDirection i_direction)
		{
			Track t = GetNextPrevTrack(i_direction);

			if (t != null)
				PlayTrack(t);
			else
				Helpers.Beep();
		}

		/// <summary>
		/// Pauses the playback.
		/// </summary>
		public void Pause()
		{
			MediaPlayer.Pause();
		}

		/// <summary>
		/// Resumes the the playback.
		/// </summary>
		public void Play()
		{
			MediaPlayer.Play();
		}

		/// <summary>
		/// Stops the playback.
		/// </summary>
		public void Stop()
		{
			MediaPlayer.Stop();
		}

		/// <summary>
		/// Closes the media file.
		/// </summary>
		public void Close()
		{
			OnPlayerStopped(null, null);
			MediaPlayer.CloseStream();
			MediaPlayer.CurrentTrack = null;
		}

		/// <summary>
		/// Plays the track.
		/// </summary>
		/// <param name="track">The track.</param>
		public void PlayTrack(Track track)
		{
			if (IsPlaying)
			{
				if(track != null && track == CurrentTrack)
					return;

				Pause();
			}

			if (!track.Exists)
			{
				MessageBoxEx.Show(Resources.SelectedTrackDoesNotExistMessage, Resources.SelectedTrackDoesNotExistTitle,
				                MessageBoxButtons.OK,
				                MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
				return;
			}
			if (track.IsPlayed)
				track.ClearPlayed();

			try
			{
				MediaPlayer.PlayTrack(track);
				OnTrackPlayStateChanged(Track.PlayMode.Play);
			}
			catch (Exception ex)
			{
				MessageBoxEx.Show(Resources.ProblemPlayingTrackMessage + ex.Message,
				                Resources.ProblemPlayingTrackTitle, MessageBoxButtons.OK,
				                MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
			}
		}

		/// <summary>
		/// Clears the playlist.
		/// </summary>
		public void ClearPlaylist()
		{
			if (IsPlayerLoaded)
			{
				if (IsPlaying)
				{
					Pause();
				}
				MediaPlayer.CurrentTrack = null;
			}

			m_PlayList.Clear();
			PlayList.Clear();
		}

		/// <summary>
		/// Called when the Application is closing.
		/// </summary>
		public void OnClosing()
		{
			MediaPlayer.OnClosing();
		}

		/// <summary>
		/// Plays or pauses the current stream.
		/// </summary>
		internal void PlayPause()
		{
			if (IsPlaying)
				Pause();
			else
				Play();
		}

		/// <summary>
		/// Plays a track from a playlist of tracks
		/// </summary>
		/// <param name="list">the playlist</param>
		/// <param name="track">the track to play</param>
		public void PlayTracks(TrackList list, Track track)
		{
			if (IsNewPlaylist(list))
			{
				ClearPlaylist();
				m_PlayList.AddRange(list);
				PlayList.SetPlaylist(m_PlayList);
			}

			Application.DoEvents();

			if (ViewManager.CurrentViewName != Helpers.ViewNames.Player)
				_trackToPlayOnActivate = track;  // start playing when the view activates
			else
			{
				_trackToPlayOnActivate = null;
				if (track != null)
					PlayTrack(track);
			}

			Focus();
			PlayList.Focus();
		}

		/// <summary>
		/// Changes the volume by an increment.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		public void ChangeVolume(Helpers.MoveDirection i_direction)
		{
			MediaPlayer.ChangeVolume(i_direction);
		}

		#endregion

		#region Public Properties

		public bool HasPlaylist
		{
			get
			{
				return  IsPlayerLoaded && m_PlayList != null && m_PlayList.Count > 0;
			}
		}

		/// <summary>
		/// Gets or sets the view mode.
		/// </summary>
		/// <value>The view mode.</value>
		public CoreHelper.PlayerView ViewMode
		{
			set
			{
				m_CurrentView = value;
				Configuration.PlayerView = m_CurrentView;
				SetView();
				Refresh();
			}

			get { return m_CurrentView; }
		}

		/// <summary>
		/// True if the media player is loaded into memory
		/// </summary>
		public bool IsPlayerLoaded
		{
			get { return !(MediaPlayer is NullPlayer); }
		}

		/// <summary>
		/// Gets the current track.
		/// </summary>
		/// <value>The current track.</value>
		public Track CurrentTrack
		{
			get
			{
					return MediaPlayer.CurrentTrack;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this instance is playing.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is playing; otherwise, <c>false</c>.
		/// </value>
		public bool IsPlaying
		{
			get { return MediaPlayer.IsPlaying; }
		}

		/// <summary>
		/// Gets a value indicating whether this instance is paused.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is playing; otherwise, <c>false</c>.
		/// </value>
		public bool IsPaused
		{
			get { return MediaPlayer.IsPaused; }
		}

		/// <summary>
		/// Gets the title.
		/// </summary>
		/// <value>The title.</value>
		public string Title
		{
			get
			{
				return string.Format("Player {0}", m_PlayList == null || m_PlayList.Count == 0 ? string.Empty : "(" + m_PlayList.Count + ")");
			}
		}

		/// <summary>
		/// Gets the view menu.
		/// </summary>
		/// <value>The view menu.</value>
		public MainMenu ViewMenu
		{
			get { return MenuManager.GetMenu(Helpers.ViewNames.Player); }
		}

		/// <summary>
		/// Gets or sets the current stream position.
		/// </summary>
		/// <value>The current stream position.</value>
		public double CurrentStreamPosition
		{
			get { return MediaPlayer.CurrentStreamPosition; }
			set { MediaPlayer.CurrentStreamPosition = value; }
		}

		/// <summary>
		/// Gets the current stream duration.
		/// </summary>
		/// <value>The current stream duration.</value>
		public double CurrentStreamDuration
		{
			get { return MediaPlayer.CurrentStreamDuration; }
		}

		/// <summary>
		/// Gets the version of windows media MediaPlayer.
		/// </summary>
		/// <value>The WMP version.</value>
		public string PlayerVersion
		{
			get { return MediaPlayer.PlayerVersion; }
		}

		/// <summary>
		/// Sets teh volume level
		/// </summary>
		public int VolumeLevel
		{
			get { return MediaPlayer.VolumeLevel; }
			set { MediaPlayer.VolumeLevel = value; }
		}

		/// <summary>
		/// THe skin manager of the current player
		/// </summary>
		public ISkinManager PlayerSkinManager
		{
			get { return m_MediaPlayerSkinManager; }
		}

		#endregion
	}
}